package com.obrock;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: Дмитрий Г. Кузнецов
 * Date: 22.10.12
 * Time: 17:02
 * To change this template use File | Settings | File Templates.
 */

public class DropboxThread implements Runnable {
    private Thread thread;
    private Context context;
    private static Database db = null;
    private static boolean terminate = false;
    private static boolean reset = false;
    private static boolean enabled = false;

    private static boolean dataChanged = false;

    public static final Object startObject = new Object();
    public static final Object syncObject = new Object();
    public static final Object loginObject = new Object();

    final Handler myHandler = new Handler();
    private static Dropbox dropBox = null;


    public DropboxThread(Context context) {
        db = new Database(context);
        this.context = context;

        dropBox = new Dropbox(context);

        thread = new Thread(this);
        thread.start();
    }

    @Override
    public void run() {

        while (!terminate) {

            try {

                dropBox.logOut();
                hideMessage();

                synchronized (startObject) {
                    startObject.wait(context.getSharedPreferences(Main.PREFERENCES_NAME, 0).getLong("SYNC_PERIOD", 600000));
                }

                if (terminate) {
                    break;
                }
                if (!enabled) {
                    continue;
                }

                showMessage(context.getResources().getString(R.string.sync_login));
                dropBox.login(context);

                if (!dropBox.mLoggedIn) {
                    synchronized (loginObject) {
                        loginObject.wait(60000 * 3);
                    }
                }

                if (!dropBox.mLoggedIn) continue;

                int updates = 0;
                List<SyncSeed> changes = new ArrayList<SyncSeed>(0);

                if (!reset) {

                    if (!dataChanged) {
                        long remoteRevision = dropBox.revision();
                        if (remoteRevision == -1) {
                            Log.e(Main.LOG_TAG, dropBox.mErrorMsg);
                            continue;
                        }

                        if (dropBox.lastRevision != 0 && remoteRevision == dropBox.lastRevision) {
                            continue;
                        }
                    }

                    showMessage(context.getResources().getString(R.string.sync_download));

                    byte[] data = dropBox.download();

                    if (data != null) {

                        showMessage(context.getResources().getString(R.string.sync_sync, "#1"));

                        ByteArrayInputStream baStream = new ByteArrayInputStream(data);
                        DataInputStream in = new DataInputStream(baStream);

                        //db.getWritableDatabase().beginTransaction();
                        while (true) {
                            try {
                                SyncSeed seed = new SyncSeed(db,in);
                                if (seed.apply()) {
                                    changes.add(seed);
                                    ++updates;
                                    showMessage(context.getResources().getString(R.string.sync_sync, "Apply " + seed.name));
                                }
                            } catch (IOException ex) {
                                break;
                            }
                        }
                        //db.getWritableDatabase().endTransaction();
                    }
                } else {

                    //db.getWritableDatabase().beginTransaction();
                    changes = db.getSnapshot();
                    for (SyncSeed seed : changes) {
                        if (seed.apply()) {
                            ++updates;
                            showMessage(context.getResources().getString(R.string.sync_sync, "Apply " + seed.name));
                        }
                    }
                    //db.getWritableDatabase().endTransaction();
                }

                if (updates > 0 || dataChanged) {

                    showMessage(context.getResources().getString(R.string.sync_sync, "#2"));

                    if (!reset) {
                        changes.addAll(db.getChanges(0));
                        Collections.sort(changes);
                    }

                    db.updateChanges(changes);
                    db.garbageCollect();

                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
                    DataOutputStream outWriter = new DataOutputStream(bufStream);

                    for (SyncSeed s : changes) {
                        s.writePacket(outWriter);
                        showMessage(context.getResources().getString(R.string.sync_sync, "Changes " + s.name));
                    }

                    showMessage(context.getResources().getString(R.string.sync_upload));

                    byte[] data = bufStream.toByteArray();
                    dropBox.upload(data);

                }

                //Main.toast("Data synchronized. Changes: " + updates);

                synchronized (syncObject) {
                    dataChanged = false;
                }

                if (updates > 0) {

                    String notify = String.format(context.getResources().getString(R.string.sync_changes), updates);
                    Main.showNotification(context, notify);

                    myHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (Main.context != null) {
                                Main.context.setMode(Main.viewMode);
                            }
                        }
                    });
                }


            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        hideMessage();
    }

    protected void showMessage(final String message) {
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                Main.showSync(message);
                /*
                if (Main.context != null) {
                    Main.context.blockStatus.setVisibility(View.VISIBLE);
                    Main.context.statusMessage.setText(message);
                }
                */
            }
        });
    }

    protected void hideMessage() {
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                Main.hideSync();
                /*
                if (Main.context != null) {
                    Main.context.blockStatus.setVisibility(View.GONE);
                }
                */
            }
        });
    }

    public static void enable() {
        enabled = true;
    }

    public static void disable() {
        enabled = false;
    }

    public static void force() {
        synchronized (startObject) {
            startObject.notify();
        }
    }

    public static void dataChanged() {
        synchronized (syncObject) {
            reset = false;
            dataChanged = true;
        }
        synchronized (startObject) {
            startObject.notify();
        }
    }

    public static void dataReset() {
        synchronized (syncObject) {
            reset = true;
            dataChanged = true;
        }
        synchronized (startObject) {
            startObject.notify();
        }
    }

    public static void checkLogin() {
        if (dropBox != null) {
            dropBox.checkLogin();
        }
    }

}


